import vtkActor from "@kitware/vtk.js/Rendering/Core/Actor"
import vtkMapper from "@kitware/vtk.js/Rendering/Core/Mapper"
import vtkColorTransferFunction from "@kitware/vtk.js/Rendering/Core/ColorTransferFunction"
import vtkDataArray from "@kitware/vtk.js/Common/Core/DataArray"
import vtkPolyData from "@kitware/vtk.js/Common/DataModel/PolyData"
// @ts-ignore
import vtkPolyDataNormals from "@kitware/vtk.js/Filters/Core/PolyDataNormals"
import vtkGlyph3DMapper from "@kitware/vtk.js/Rendering/Core/Glyph3DMapper"
import vtkArrowSource from "@kitware/vtk.js/Filters/Sources/ArrowSource"

import { BActor } from "../Core/BActor"
import { ACTOR_TYPE, Face, PointsActor, User, VertexPoint } from "../"
import { Vector3 } from "@kitware/vtk.js/types"
import { TEdge } from "../Core/TEdge"
import { OutLineActor } from "../Core/OutlineActor"
import { Vector3 as IVector3 } from "stl-typescript"

import { calculatePolygonCenter } from "../Math"
import vtkPoints from "@kitware/vtk.js/Common/Core/Points"
import { ColorMode } from "@kitware/vtk.js/Rendering/Core/Mapper/Constants"

export enum VTActorRenderType {
  HIDDEN = "HIDDEN",
  POINTS = "POINTS",
  WIREFRAME = "WIREFRAME",
  SURFACE = "SURFACE",
  SURFACE_WITH_EDGE = "SURFACE_WITH_EDGE",
}

/**
 * ! 📌重要内容说明 关于mapper初始化参数
 * @ static: 是否启用静态映射,如果为true,只在初始化时映射一次数据。
 * @ scalarVisibility: 是否启用标量可视化。
 * @ scalarRange: 标量值的范围。
 * @ useLookupTableScalarRange: 是否使用查找表的标量范围。
 * @ colorMode: 颜色模式,控制RGB或RGBA。
 * @ scalarMode: 标量模式,控制使用何种标量进行着色。
 * @ arrayAccessMode: 数组访问模式,控制数据访问的存储顺序。
 * @ renderTime: 渲染时间变量,用于动画等。
 * @ fieldDataTupleId: 字段数据元组id,控制使用字段数据的哪部分。
 * @ interpolateScalarsBeforeMapping: 在映射前进行标量插值。
 * @ forceCompileOnly: 强制只编译 不执行着色器。
 * @ useInvertibleColors: 使用可逆颜色查找表。
 * @ customShaderAttributes: 自定义着色器属性。
 */

export interface VTActorOptions {
  useLookupTableScalarRange?: boolean
  initialScalarValue?: number[]
  color?: number[]
  defaultCellColor?: number[]
  // * 边缘数据
  outlines?: vtkPolyData
  vertexs?: VertexPoint[]
  edgeData?: TEdge
}

export class VTActor extends BActor {
  readonly type = ACTOR_TYPE.VT_ACTOR
  createdBy = "default"
  instance = vtkActor.newInstance()
  lookupTable: vtkColorTransferFunction = vtkColorTransferFunction.newInstance()
  mapper: vtkMapper
  originData: any
  options: VTActorOptions
  // * 特殊属性
  userdata: any = {}
  // todo
  faces: Face[] = []
  faceIds: number[] = []
  vertexes: VertexPoint[]
  isMesh = false
  isMeshPoint = false
  // * 显示属性
  outlines: OutLineActor | undefined
  vertexAc: PointsActor | undefined
  private _normalsActor: vtkActor | undefined
  private _isShowNormals = false
  // 面的默认颜色为黄色，但是可以修改
  defaultColor: number[] = [255, 255, 0, 255]

  // * 是否需要触发更新
  triggerNormalUpdate = 0

  // * 法向量数据
  normals: Float32Array | null = null
  // mark
  constructor(polyData: any, options: VTActorOptions = {}) {
    super()
    this.options = options
    this.originData = polyData
    this.vertexes = options.vertexs ?? []
    if (options.defaultCellColor) {
      this.setDefaultColor(options.defaultCellColor)
    }
    this.mapper = this.initMapper()
    this.instance.setMapper(this.mapper)
    // # 纯色模式渲染
    // this.instance.getProperty().setLighting(false);
    if (options.edgeData) {
      this.createOutlinesAc(options.edgeData)
    }
    if (options.vertexs) {
      this.createVertexAc(options.vertexs)
    }
  }

  setDefaultColor(color: number[]) {
    this.defaultColor = color
  }

  createOutlinesAc(data: TEdge) {
    const outlines = new OutLineActor(data)
    outlines.setRenderType(VTActorRenderType.SURFACE)
    outlines.instance.getProperty().setLineWidth(2)
    outlines.hiddenEdgeColor()
    this.outlines = outlines
  }
  createVertexAc(vertexs: VertexPoint[]) {
    const points: Vector3[] = []
    vertexs.forEach((ele) => {
      points.push(ele.XYZ as Vector3)
    })
    this.vertexAc = new PointsActor({
      points,
    })
  }

  initMapper(): vtkMapper {
    const mapper = vtkMapper.newInstance({
      interpolateScalarsBeforeMapping: false,
      useLookupTableScalarRange: false,
      scalarVisibility: true,
      colorMode: ColorMode.DIRECT_SCALARS,
    })
    mapper.setScalarModeToUseCellData()
    mapper.setInputData(this.originData)
    const inputData = mapper.getInputData()
    if (!inputData.getCells()) {
      inputData.buildCells()
    }
    if (this.options.color) {
      this.instance
        .getProperty()
        .setColor(
          this.options.color[0],
          this.options.color[1],
          this.options.color[2]
        )
    } else {
      if (this.options.initialScalarValue !== undefined) {
        this.createCellColor(mapper, this.options.initialScalarValue)
      } else {
        this.createCellColor(mapper)
      }
    }
    return mapper
  }

  @User()
  setRenderType(type: VTActorRenderType) {
    if (type === VTActorRenderType.HIDDEN) {
      this.instance.setVisibility(false)
      return
    }
    this.instance.setVisibility(true)
    let representation: number
    let edgeVisibility: number

    switch (type) {
      case VTActorRenderType.POINTS:
        representation = 0
        edgeVisibility = 0
        break
      case VTActorRenderType.WIREFRAME:
        representation = 1
        edgeVisibility = 0
        break
      case VTActorRenderType.SURFACE:
        representation = 2
        edgeVisibility = 0
        break
      case VTActorRenderType.SURFACE_WITH_EDGE:
        representation = 2
        edgeVisibility = 1
        break
      default:
        representation = 2
        edgeVisibility = 0
        break
    }

    this.instance.getProperty().set({
      representation,
      edgeVisibility,
    })
  }

  public set opacity(v: number) {
    this.instance.getProperty().setOpacity(v)
  }

  public get opacity(): number {
    return this.instance.getProperty().getOpacity()
  }

  findCellById(id: number) {
    return this.mapper.getInputData().getCell(id)
  }

  createCellColor(mapper: vtkMapper, tuple: number[] = this.defaultColor) {
    const inputData = mapper.getInputData()
    const pd = inputData.getCellData()
    const colors = pd.getScalars()
    if (!colors) {
      const cellsInfo = inputData.getCells().get()
      const npts = cellsInfo.size
      const scalars = vtkDataArray.newInstance({
        name: "rgba",
        numberOfComponents: 4,
        values: new Uint8ClampedArray(npts * 4),
      })
      const rgba = tuple as number[]
      for (let i = 0; i < npts; ++i) {
        scalars.setTuple(i, [...rgba])
      }
      mapper.getInputData().getCellData().setScalars(scalars)
    }
  }

  setCellColor(id: number, rgba: number[] = [0, 255, 0, 255]) {
    const mapper = this.initMapper()
    const sc = mapper.getInputData().getCellData().getScalars()
    sc.setTuple(id, [...rgba])
    mapper.getInputData().getCellData().setScalars(sc)
    this.instance.setMapper(mapper)
    this.mapper = mapper
  }

  // 将entity恢复为默认颜色
  setCells2defaultColor(ids: number[]) {
    this.setCellsColor(ids, this.defaultColor)
  }

  setCellsColor(ids: number[], rgba: number[]) {
    const mapper = this.initMapper()
    const sc = mapper.getInputData().getCellData().getScalars()
    ids.forEach((id, index) => {
      sc.setTuple(id, [...rgba])
    })
    mapper.getInputData().getCellData().setScalars(sc)
    this.instance.setMapper(mapper)
    this.mapper = mapper
  }

  resetCellsColor(rgba: number[] = this.defaultColor) {
    const mapper = this.initMapper()
    const sc = mapper.getInputData().getCellData().getScalars()
    const count = sc.getData().length
    for (let i = 0; i < count; i++) {
      sc.setTuple(i, [...rgba])
    }
    mapper.getInputData().getCellData().setScalars(sc)
    this.instance.setMapper(mapper)
    this.mapper = mapper
  }

  serialize() {
    return 0
  }

  deserialize() {
    return 0
  }

  get visible() {
    return this.instance.getVisibility()
  }

  set visible(vis: boolean) {
    this.instance.setVisibility(vis)
  }

  /**
   *
   * @param virtual {boolean} 是否只计算数据 不创建对象
   */
  createNormalActor(virtual = false) {
    if (virtual && this.normals) return
    // 计算单元中心
    const cellVertexes = this.getCellVertexes()
    const centers: IVector3[] = []
    cellVertexes.forEach((item) => {
      centers.push(calculatePolygonCenter(item))
    })

    // 计算法向量
    const normalsFilter = vtkPolyDataNormals.newInstance()
    normalsFilter.setComputeCellNormals(true)
    normalsFilter.setComputePointNormals(false)
    normalsFilter.setInputData(this.originData)
    normalsFilter.update()
    const cellNormals = normalsFilter.getOutputData().getCellData().getNormals()

    // 构建法向量顶点数据
    const pd = vtkPolyData.newInstance()
    const TMP_arr = new Float32Array(centers.length * 3).fill(0.0)
    const points = vtkPoints.newInstance()
    const w = new Float32Array(cellNormals.getData().length)
    cellNormals.getData().forEach((item: any, index: number) => {
      w[index] = 0 - item
    })
    if (virtual) {
      this.normals = w
      return
    }
    const factorSize = 15
    centers.forEach((v: IVector3, index: number) => {
      const tempX = w[3 * index]
      const tempY = w[3 * index + 1]
      const tempZ = w[3 * index + 2]
      TMP_arr[3 * index] = v.x + (tempX * factorSize * -1) / 1.5
      TMP_arr[3 * index + 1] = v.y + (tempY * factorSize * -1) / 1.5
      TMP_arr[3 * index + 2] = v.z + (tempZ * factorSize * -1) / 1.5
    })

    points.setData(TMP_arr, 3)
    pd.setPoints(points)

    pd.getPointData().setNormals(
      vtkDataArray.newInstance({
        name: "Normals",
        values: [...w],
        numberOfComponents: 3, // 法线向量的分量个数
      })
    )
    const arrowSource = vtkArrowSource.newInstance()
    const glyphMapper = vtkGlyph3DMapper.newInstance({
      useLookupTableScalarRange: false,
    })
    glyphMapper.setInputData(pd)
    glyphMapper.setSourceConnection(arrowSource.getOutputPort())
    glyphMapper.setOrientationModeToDirection()
    glyphMapper.setOrientationArray("Normals")
    glyphMapper.setScaleModeToScaleByMagnitude()
    //@ts-ignore
    glyphMapper.setScaleArray("Normals")
    glyphMapper.setScaleFactor(factorSize)
    const glyphActor = vtkActor.newInstance()
    glyphActor.setMapper(glyphMapper)
    glyphActor.getProperty().setColor(1.0, 0.0, 0.0)
    this._normalsActor = glyphActor
  }

  normalsOn() {
    if (this._isShowNormals) return
    if (this._normalsActor === undefined) {
      this.createNormalActor()
    }
    this._isShowNormals = true
    this.triggerNormalUpdate = 1
  }

  normalsOff() {
    if (!this._isShowNormals) return
    this._isShowNormals = false
    this.triggerNormalUpdate = 1
  }

  get isShowNormals() {
    return this._isShowNormals
  }

  getNormalActor() {
    return this._normalsActor
  }

  getCellVertexes(): IVector3[][] {
    const arr: any = []
    // 获取单元格和顶点
    const points = this.originData.getPoints()
    const polys = this.originData.getPolys()

    // 获取每个单元格的顶点坐标
    const cellSize = polys.getNumberOfCells()
    const cellArray = polys.getData()
    let cellOffset = 0
    for (let i = 0; i < cellSize; i++) {
      const cellLength = cellArray[cellOffset]
      // console.log(`Cell ${i}: cellSize ${cellSize}`);
      const a: any = []
      for (let j = 1; j <= cellLength; j++) {
        const pointId = cellArray[cellOffset + j]
        const point = points.getPoint(pointId)
        a.push(new IVector3(point[0], point[1], point[2]))
      }
      arr.push(a)
      cellOffset += cellLength + 1
    }

    return arr
  }
}
